package com.sumec.client;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * ucenter接口调用类
 */
@Component
public class Client extends PHPFunctions implements InitializingBean {
	private static String UC_CLIENT_RELEASE = "20170101";
	private String ucApiFunc;

	@Value("${uc_api}")
	private String ucApi;
	@Value("${uc_ip}")
	private String ucIp;
	@Value("${uc_connect}")
	private String ucConnect;
	@Value("${uc_key}")
	private String ucKey;
	@Value("${uc_appid}")
	private String ucAppid;

	@Override
	public void afterPropertiesSet() {
		ucApiFunc = ucConnect.equals("mysql") ? "uc_api_mysql" : "uc_api_post";
	}

	protected String ucSerialize(String arr, int htmlon) {
		return arr;
	}

	protected String ucDeserialize(String str) {
		return str;
	}

	protected String ucStripslashes(String str) {
		return str;
	}

	@SuppressWarnings("unchecked")
	public String ucApiPost(String module, String action, Map<String, Object> params) {
		StringBuilder reqStr = new StringBuilder();
		String sep = "";
		for (String key : params.keySet()) {
			Object value = params.get(key);
			key = urlencode(key);

			if (value.getClass().isAssignableFrom(Map.class)) {
				String subReqStr = "";
				String subSep = "";
				for (String subKey : ((Map<String, Object>) value).keySet()) {
					Object subValue = ((Map<String, Object>) value).get(subKey);
					subKey = urlencode(subKey);
					subReqStr += subSep + "{" + key + "}[" + subKey + "]=" + urlencode(ucStripslashes(String.valueOf(subValue)));
					subSep = "&";
				}
				reqStr.append(sep).append(subReqStr);
			} else {
				reqStr.append(sep).append(key).append("=").append(urlencode(ucStripslashes(String.valueOf(value))));
			}
			sep = "&";
		}
		String postdata = ucApiRequestdata(module, action, reqStr.toString(), "");
		return ucFopen2(ucApi + "/index.php", 500000, postdata, "", true, ucIp, 20, true);
	}

	/**
	 * 构造发送给用户中心的请求数据
	 *
	 * @param module 请求的模块
	 * @param action 请求的动作
	 * @param params 参数（会加密的方式传送）
	 * @param extra 附加参数（传送时不加密）
	 * @return string
	 */
	protected String ucApiRequestdata(String module, String action, String params, String extra) {
		String input = ucApiInput(params);
		return "m=" + module + "&a=" + action + "&inajax=2&release=" + UC_CLIENT_RELEASE + "&input=" + input + "&appid=" + ucAppid + extra;
	}

	protected String ucApiUrl(String module, String action, String param, String extra) {
		return ucApi + "/index.php?" + ucApiRequestdata(module, action, param, extra);
	}

	/**
	 * 参数加密
	 * @param data
	 * @return
	 */
	public String ucApiInput(String data) {
		return urlencode(ucAuthCode(data + "&agent=" + md5("") + "&time=" + time(), "ENCODE", ucKey));
	}

	/**
	 * MYSQL 方式取指定的模块和动作的数据
	 *
	 * @param model 请求的模块
	 * @param action 请求的动作
	 * @param args 参数（会加密的方式传送）
	 * @return mix
	 */
	public String ucApiMysql(String model, String action, Map<String, Object> args) {
		if (action.charAt(0) != '_') {
			return null;
		} else {
			return "";
		}
	}

	/**
	 * 字符串加密以及解密函数
	 *
	 * @param origin 原文或者密文
	 * @param desc 操作(ENCODE | DECODE), 默认为 DECODE
	 * @return 处理后的 原文或者 经过 base64_encode 处理后的密文
	 */
	public String ucAuthCode(String origin, String desc) {
		return ucAuthCode(origin, desc, null);
	}

	public String ucAuthCode(String origin, String desc, String key) {
		return ucAuthCode(origin, desc, key, 0);
	}

	public String ucAuthCode(String origin, String desc, String key, int expiry) {
		int ckeyLength = 4; // note 随机密钥长度 取值 0-32;
		// note 加入随机密钥，可以令密文无任何规律，即便是原文和密钥完全相同，加密结果也会每次不同，增大破解难度。
		// note 取值越大，密文变动规律越大，密文变化 = 16 的 ckeyLength 次方
		// note 当此值为 0 时，则不产生随机密钥
		key = md5(key != null ? key : ucKey);
		String keya = md5(substr(key, 0, 16));
		String keyb = md5(substr(key, 16, 16));
		String keyc = ckeyLength > 0 ? (desc.equals("DECODE") ? substr(origin, 0, ckeyLength) : substr(md5(microtime()), -ckeyLength)) : "";

		String cryptkey = keya + md5(keya + keyc);
		int keyLength = cryptkey.length();

		origin = desc.equals("DECODE") ? base64_decode(substr(origin, ckeyLength))
				: sprintf("%010d", expiry > 0 ? expiry + time() : 0) + substr(md5(origin + keyb), 0, 16) + origin;
		int strLength = origin.length();

		StringBuilder result1 = new StringBuilder();

		int[] box = new int[256];
		for (int i = 0; i < 256; i++) {
			box[i] = i;
		}

		int[] rndkey = new int[256];
		for (int i = 0; i <= 255; i++) {
			rndkey[i] = (int) cryptkey.charAt(i % keyLength);
		}

		int j = 0;
		for (int $i = 0; $i < 256; $i++) {
			j = (j + box[$i] + rndkey[$i]) % 256;
			int $tmp = box[$i];
			box[$i] = box[j];
			box[j] = $tmp;
		}

		j = 0;
		int a = 0;
		for (int i = 0; i < strLength; i++) {
			a = (a + 1) % 256;
			j = (j + box[a]) % 256;
			int tmp = box[a];
			box[a] = box[j];
			box[j] = tmp;

			result1.append((char) (((int) origin.charAt(i)) ^ (box[(box[a] + box[j]) % 256])));

		}

		if (desc.equals("DECODE")) {
			String result = result1.substring(0, result1.length());
			if ((Integer.parseInt(substr(result.toString(), 0, 10)) == 0
					|| Long.parseLong(substr(result.toString(), 0, 10)) - time() > 0)
					&& substr(result.toString(), 10, 16)
							.equals(substr(md5(substr(result.toString(), 26) + keyb), 0, 16))) {
				return substr(result.toString(), 26);
			} else {
				return "";
			}
		} else {
			return keyc + base64_encode(result1.toString()).replaceAll("=", "");
		}
	}

	/**
	 * 远程打开URL
	 * 
	 * @param url 打开的url， 如 http://www.baidu.com/123.htm
	 * @param limit 取返回的数据的长度
	 * @param post 要发送的 POST 数据，如uid=1&password=1234
	 * @param cookie 要模拟的 COOKIE 数据，如uid=123&auth=a2323sd2323
	 * @param bysocket TRUE/FALSE 是否通过SOCKET打开
	 * @param ip IP地址
	 * @param timeout 连接超时时间
	 * @param block 是否为阻塞模式 defaul valuet:true
	 * @return 取到的字符串
	 */
	protected String ucFopen2(String url, int limit, String post, String cookie, boolean bysocket, String ip, int timeout, boolean block) {
		url += url.indexOf("?") > 0 ? "&" : "?" + "__times__=1";
		return ucFopen(url, limit, post, cookie, bysocket, ip, timeout, block);
	}

	protected String ucFopen(String url, int limit, String post, String cookie, boolean bysocket, String ip, int timeout, boolean block) {
		String returnStr = "";

		URL matches;
		String host = "";
		String path = "";
		int port = 80;
		try {
			matches = new URL(url);
			host = matches.getHost();
			path = matches.getPath() != null ? matches.getPath() + (matches.getQuery() != null ? "?" + matches.getQuery() : "") : "/";
			if (matches.getPort() > 0) {
				port = matches.getPort();
			}
		} catch (MalformedURLException e1) {
		}

		StringBuilder outStr = new StringBuilder();
		if (post != null && post.length() > 0) {
			outStr.append("POST ").append(path).append(" HTTP/1.0\r\n");
			outStr.append("Accept: */*\r\n");
			outStr.append("Accept-Language: zh-cn\r\n");
			outStr.append("Content-Type: application/x-www-form-urlencoded\r\n");
			outStr.append("User-Agent: \r\n");
			outStr.append("Host: ").append(host).append("\r\n");
			outStr.append("Content-Length: ").append(post.length()).append("\r\n");
			outStr.append("Connection: Close\r\n");
			outStr.append("Cache-Control: no-cache\r\n");
			outStr.append("Cookie: \r\n\r\n");
			outStr.append(post);
		} else {
			outStr.append("GET path HTTP/1.0\r\n");
			outStr.append("Accept: */*\r\n");
			// outStr .= "Referer: $boardurl\r\n";
			outStr.append("Accept-Language: zh-cn\r\n");
			outStr.append("User-Agent: Java/1.5.0_01\r\n");
			outStr.append("Host: host\r\n");
			outStr.append("Connection: Close\r\n");
			outStr.append("Cookie: cookie\r\n\r\n");
		}

		try {
			Socket socket = new Socket(ip != null && ip.length() > 10 ? ip : host, port);
			if (!socket.isConnected()) {
				return "";// note $errstr : $errno \r\n
			} else {
				OutputStream os = socket.getOutputStream();
				os.write(outStr.toString().getBytes());

				InputStream ins = socket.getInputStream();
				BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
				while (true) {
					String header = reader.readLine();
					if (header == null || header.equals("") || header == "\r\n" || header == "\n") {
						break;
					}
				}

				while (true) {
					String data = reader.readLine();
					if (data == null || data.equals("")) {
						break;
					} else {
						returnStr += data;
					}
				}

				socket.close();
			}
		} catch (IOException e) {
		}
		return returnStr;
	}

	public String ucAppLs() {
		String result = callUserFunc(ucApiFunc, "app", "ls", null);
		return ucConnect.equals("mysql") ? result : ucDeserialize(result);
	}

	/**
	 * 用户注册
	 *
	 * @param username 用户名
	 * @param password 密码
	 * @param email Email
	 * @return int -1 : 用户名不合法 -2 : 包含不允许注册的词语 -3 : 用户名已经存在 -4 : email 格式有误 -5 :
	 *         email 不允许注册 -6 : 该 email 已经被注册 >1 : 表示成功，数值为 UID
	 */
	public String ucUserRegister(String username, String password, String email) {
		return ucUserRegister(username, password, email, "", "");
	}

	public String ucUserRegister(String username, String password, String email, String questionid, String answer) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("username", username);
		args.put("password", password);
		args.put("email", email);
		args.put("questionid", questionid);
		args.put("answer", answer);
		return callUserFunc(ucApiFunc, "user", "register", args);
	}

	/**
	 * 用户登陆检查
	 *
	 * @param username 用户名/uid
	 * @param password 密码
	 * @return array (uid/status, username, password, email) 数组第一项 1 : 成功 -1 :用户不存在,或者被删除 -2 : 密码错
	 */
	public String ucUserLogin(String username, String password) {
		return ucUserLogin(username, password, 0, 0);
	}

	public String ucUserLogin(String username, String password, int isuid, int checkques) {
		return ucUserLogin(username, password, isuid, checkques, "", "");
	}

	public String ucUserLogin(String username, String password, int isuid, int checkques, String questionid, String answer) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("username", username);
		args.put("password", password);
		args.put("isuid", isuid);
		args.put("checkques", checkques);
		args.put("questionid", questionid);
		args.put("answer", answer);
		String result = callUserFunc(ucApiFunc, "user", "login", args);
		return ucConnect.equals("mysql") ? result : ucDeserialize(result);
	}

	/**
	 * 进入同步登录代码
	 *
	 * @param uid 用户ID
	 * @return string HTML代码
	 */
	public String ucUserSynlogin(int uid) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("uid", uid);
		return ucApiPost("user", "synlogin", args);
	}

	/**
	 * 进入同步登出代码
	 *
	 * @return string HTML代码
	 */
	public String ucUserSynlogout() {
		return ucApiPost("user", "synlogout", null);
	}

	/**
	 * 取得用户数据
	 *
	 * @param username 用户名
	 * @param isuid 是否为UID
	 * @return array (uid, username, email)
	 */
	public String ucGetUser(String username, int isuid) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("username", username);
		args.put("isuid", isuid);
		String res = callUserFunc(ucApiFunc, "user", "get_user", args);
		return ucConnect.equals("mysql") ? res : ucDeserialize(res);
	}

	/**
	 * 编辑用户
	 *
	 * @param username 用户名
	 * @param oldpw 旧密码
	 * @param newpw 新密码
	 * @param email Email
	 * @param ignoreoldpw 是否忽略旧密码, 忽略旧密码, 则不进行旧密码校验.
	 * @param questionid 安全提问
	 * @param answer 安全提问答案
	 * @return int 1 : 修改成功 0 : 没有任何修改 -1 : 旧密码不正确 -4 : email 格式有误 -5 : email 不允许注册
	 *         -6 : 该 email 已经被注册 -7 : 没有做任何修改 -8 : 受保护的用户，没有权限修改
	 */
	public String ucUserEdit(String username, String oldpw, String newpw, String email, int ignoreoldpw, String questionid, String answer) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("username", username);
		args.put("oldpw", oldpw);
		args.put("newpw", newpw);
		args.put("email", email);
		args.put("ignoreoldpw", ignoreoldpw);
		args.put("questionid", questionid);
		args.put("answer", answer);
		return callUserFunc(ucApiFunc, "user", "edit", args);
	}

	/**
	 * 删除用户
	 *
	 * @param uid 用户的 UID
	 * @return int >0 : 成功 0 : 失败
	 */
	public String ucUserDelete(String uid) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("uid", uid);
		return callUserFunc(ucApiFunc, "user", "delete", args);
	}

	/**
	 * 删除用户头像
	 *
	 * @param uid 用户的 UID
	 */
	public String ucUserDeleteavatar(String uid) {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("uid", uid);
		return ucApiPost("user", "deleteavatar", args);
	}
}